/*
 * Copyright (C) 2014-2016 Qiujuer <qiujuer@live.cn>
 * WebSite http://www.qiujuer.net
 * Author qiujuer
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package net.qiujuer.genius.ui.drawable;

import ohos.agp.components.Component;
import ohos.agp.render.Arc;
import ohos.agp.render.Canvas;
import ohos.agp.render.Paint;
import ohos.agp.utils.Rect;
import ohos.agp.utils.RectFloat;

/**
 * A drawable to draw loading form Circle Type
 *
 * @since 2021-05-13
 */
public class LoadingCircleDrawable extends LoadingDrawable {
    private static final int ANGLE_ADD = 5;
    private static final int MIN_ANGLE_SWEEP = 3;
    private static final int MAX_ANGLE_SWEEP = 255;
    private static int DEFAULT_SIZE = 56;
    private int mMinSize = DEFAULT_SIZE;
    private int mMaxSize = DEFAULT_SIZE;
    private RectFloat mOval = new RectFloat();
    private float mStartAngle = 0;
    private float mSweepAngle = 0;
    private int mAngleIncrement = -3;
    private Arc arc = new Arc(0, 360, false);
    private Component mComponent;

    /** 构造函数
     * @param component 控件
     */
    public LoadingCircleDrawable(Component component) {
        super();
        this.mComponent = component;
        initBounds();
        this.mComponent.addDrawTask(this, Component.DrawTask.BETWEEN_CONTENT_AND_FOREGROUND);
        mForegroundPaint.setStrokeCap(Paint.StrokeCap.ROUND_CAP);
        setmCallback(mCallback);
    }

    /** 构造函数
     * @param component 控件
     * @param minSize 最小大小
     * @param maxSize 最大大小
     */
    public LoadingCircleDrawable(Component component, int minSize, int maxSize) {
        super();
        this.mComponent = component;
        mMinSize = minSize;
        mMaxSize = maxSize;
        setmCallback(mCallback);
    }

    private void initBounds() {
        final int paddingLeft = mComponent.getPaddingLeft();
        final int paddingTop = mComponent.getPaddingTop();
        final int paddingRight = mComponent.getPaddingRight();
        final int paddingBottom = mComponent.getPaddingBottom();
        final int w = mComponent.getWidth();
        final int h = mComponent.getHeight();
        onBoundsChange(paddingLeft, paddingTop, w - paddingRight, h - paddingBottom);
    }

    private final Callback mCallback = new Callback() {
        public void invalidate() {
            if (mComponent != null) {
                mComponent.invalidate();
            }
        }
    };

    @Override
    public int getIntrinsicHeight() {
        float maxLine = Math.max(mBackgroundPaint.getStrokeWidth(), mForegroundPaint.getStrokeWidth());
        int size = (int) (maxLine * 2 + 10);
        return Math.min(mMaxSize, Math.max(size, mMinSize));
    }

    @Override
    public int getIntrinsicWidth() {
        float maxLine = Math.max(mBackgroundPaint.getStrokeWidth(), mForegroundPaint.getStrokeWidth());
        int size = (int) (maxLine * 2 + 10);
        return Math.min(mMaxSize, Math.max(size, mMinSize));
    }

    @Override
    protected void reset() {
    }

    @Override
    public void onBoundsChange(int left, int top, int right, int bottom) {
        Rect bounds = new Rect(left, top, right, bottom);
        super.setBounds(bounds);
        if (bounds.left == 0 && bounds.top == 0 && bounds.right == 0 && bounds.bottom == 0) {
            return;
        }
        final int centerX = bounds.getCenterX();
        final int centerY = bounds.getCenterY();
        final int radius = Math.min(bounds.getHeight(), bounds.getWidth()) >> 1;
        final int maxStrokeRadius = ((int) Math.max(getForegroundLineSize(), getBackgroundLineSize()) >> 1) + 1;
        final int areRadius = radius - maxStrokeRadius;
        mOval.left = centerX - areRadius;
        mOval.top = centerY - areRadius;
        mOval.right = centerX + areRadius;
        mOval.bottom = centerY + areRadius;
    }

    @Override
    protected void onProgressChange(float progress) {
        mStartAngle = 0;
        mSweepAngle = 360 * progress;
    }

    @Override
    protected void onRefresh() {
        final float angle = ANGLE_ADD;
        mStartAngle += angle;
        if (mStartAngle > 360) {
            mStartAngle -= 360;
        }
        if (mSweepAngle > MAX_ANGLE_SWEEP) {
            mAngleIncrement = -mAngleIncrement;
        } else if (mSweepAngle < MIN_ANGLE_SWEEP) {
            mSweepAngle = MIN_ANGLE_SWEEP;
            return;
        } else if (mSweepAngle == MIN_ANGLE_SWEEP) {
            mAngleIncrement = -mAngleIncrement;
            getNextForegroundColor();
        }
        mSweepAngle += mAngleIncrement;
    }

    @Override
    protected void drawBackground(Canvas canvas, Paint backgroundPaint) {
        canvas.drawArc(mOval, arc, backgroundPaint);
    }

    @Override
    protected void drawForeground(Canvas canvas, Paint foregroundPaint) {
        canvas.drawArc(mOval, new Arc(mStartAngle, -mSweepAngle, false), foregroundPaint);
    }
}